package net.gldwork.facadegen.util;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileManager.Location;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;

import org.apache.commons.lang3.StringUtils;

public class DynamicSourceUtil {
	public static Class<?> dynamicLoadClass(String classFilePath,
			String fullClassName) {

		try {
			return new MyClassLoader(DynamicSourceUtil.class.getClassLoader(),
					classFilePath, fullClassName).loadClass(fullClassName);
		} catch (ClassNotFoundException e) {

			e.printStackTrace();
		}
		return null;

	}

	public static Class<?> dynamicCompileAndLoad(String fullClassName,
			String classStr, String outputPath, List<String> lstClasspath) {

		if (!outputPath.endsWith("/")) {
			outputPath += "/";
		}
		boolean b = dynamicCompile(fullClassName, classStr, outputPath,
				lstClasspath);
		if (!b)
			return null;
		return dynamicLoadClass(outputPath + fullClassName.replace('.', '/')
				+ ".class", fullClassName);

	}

	public static boolean dynamicCompile(String fullClassName, String classStr,
			String classPath, List<String> lstClasspath) {
		JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
		StandardJavaFileManager fileManager = jc.getStandardFileManager(null,
				null, null);
		Location location = StandardLocation.CLASS_OUTPUT;

		File destFile = new File(classPath);
		if (!destFile.exists())
			destFile.mkdirs();
		File[] outputs = new File[] { destFile };
		try {
			fileManager.setLocation(location, Arrays.asList(outputs));

		} catch (IOException e) {
			e.printStackTrace();
		}

		JavaFileObject jfo = new JavaSourceFromString(fullClassName,
				classStr.toString());
		JavaFileObject[] jfos = new JavaFileObject[] { jfo };
		Iterable<? extends JavaFileObject> compilationUnits = Arrays
				.asList(jfos);

		List<String> options = null;
		if (lstClasspath != null) {
			options = new ArrayList<String>();
			options.add("-classpath");

			options.add(StringUtils.join(lstClasspath, File.pathSeparator));

		}

		return jc.getTask(null, fileManager, null, options, null,
				compilationUnits).call();

	}

}

class JavaSourceFromString extends SimpleJavaFileObject {
	final String code;

	JavaSourceFromString(String name, String code) {
		super(URI.create("string:///" + name.replace('.', '/')
				+ Kind.SOURCE.extension), Kind.SOURCE);
		this.code = code;
	}

	@Override
	public CharSequence getCharContent(boolean ignoreEncodingErrors) {
		return code;
	}
}

class MyClassLoader extends ClassLoader {

	private String path;
	private String className;

	public MyClassLoader(ClassLoader parent, String fullClassPath,
			String fullClassName) {
		super(parent);
		path = fullClassPath;
		className = fullClassName;
	}

	public Class<?> loadClass(String name) throws ClassNotFoundException {
		if (!className.equals(name)) {
			return super.loadClass(name);
		}
		try {
			// String url = "file:" + path;
			File f = new File(path);
			// URL myUrl = new URL(url);
			// URLConnection connection = myUrl.openConnection();
			InputStream input = new FileInputStream(f);
			ByteArrayOutputStream buffer = new ByteArrayOutputStream();
			int data = input.read();

			while (data != -1) {
				buffer.write(data);
				data = input.read();
			}

			input.close();

			byte[] classData = buffer.toByteArray();

			return defineClass(className, classData, 0, classData.length);

		} catch (Exception e) {
			e.printStackTrace();
		}

		return null;
	}

}
